package com.github.tvbox.osc.viewmodel;

import android.os.Message;

import com.github.tvbox.osc.ui.activity.RecWordHelper;
import com.lzy.okgo.OkGo;
import com.orhanobut.logger.Logger;


import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import io.github.source.Sniffer;
import io.github.source.bean.Site;
import io.github.source.config.VodConfig;
import io.github.source.media.bean.AMedia;
import io.github.source.media.bean.MediaBean;
import io.github.source.media.bean.MediaCallback;
import io.github.source.media.bean.MediaCategory;
import io.github.source.media.bean.MediaPlay;
import io.github.source.media.bean.MediaQuery;
import io.github.source.media.bean.MediaResult;

/**
 * 内部做了异步处理，将非UI部都通过异步调用，再同步回调
 */
public class SourceViewModel extends ViewModel {
    public MutableLiveData<MediaResult<List<MediaCategory>>> categoryResult;
    public MutableLiveData<MediaResult<List<MediaBean>>> MediaBeanResult;
    public MutableLiveData<MediaResult<List<MediaBean>>> MediaDetailResult;
    public MutableLiveData<MediaResult<List<MediaBean>>> MediaSearchResult;
    public MutableLiveData<MediaResult<MediaPlay>> MediaPlayResult;
    public MutableLiveData<MediaResult<Message>> mMediaCallback;//播放回调
    public MutableLiveData<List<String>> queryWord;

    public SourceViewModel() {
        categoryResult = new MutableLiveData<>();
        MediaBeanResult = new MutableLiveData<>();
        MediaDetailResult = new MutableLiveData<>();
        MediaSearchResult = new MutableLiveData<>();
        mMediaCallback = new MutableLiveData<>();
        MediaPlayResult = new MutableLiveData<>();
        queryWord = new MutableLiveData<>();

        AMedia.registerListener(new MediaCallback() {
            @Override
            public void onMessage() {
                Logger.d("TAG MESSAGE");

            }

            @Override
            public void onMessage(String msg) {
                Logger.d("TAG MESSAGE=" + msg);

            }

            @Override
            public void onMessage(Message msg) {
                Logger.d("TAG MESSAGE=" + msg);
            }
        });
    }

    public static ExecutorService executorService = null;
    public static ExecutorService executorServicePlay = null;

    public Future<?> execute(Runnable runnable) {
        Future<?> future = executorService().submit(() -> VodConfig.get().initConfig((result, msg) -> {
            try {
                Logger.d("code:" + result + ", msg:" + msg);
                if (Thread.interrupted()) return;
                runnable.run();
            } catch (Throwable e) {
                if (Thread.interrupted()) return;
                e.printStackTrace();
            }
        }));
        return future;
    }

    private synchronized List<Future<?>> getAddFutures(Future<?> future) {
        futures.add(future);
        //try Clear
        int size = futures.size();
        if (size > 8) {
            tryClearFutures();
        }
        return futures;
    }

    private void tryClearFutures() {
        for (Iterator<Future<?>> iterator = futures.iterator(); iterator.hasNext(); ) {
            Future<?> aFuture = iterator.next();
            if (aFuture.isDone()) {
                aFuture.cancel(true);
                iterator.remove();//注意多线抛出 ConcurrentModificationException
            }
        }
    }

    public synchronized ExecutorService executorService() {
        if (executorService == null) {
            //TODO 线程池的相关概念 需要理解
            //TODO 核心线程 最大线程 非核心线程闲置60秒回收 任务队列
            executorService = new ThreadPoolExecutor(4, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory("SourceViewModel Dispatcher", false));
        }
        return executorService;
    }

    /**
     * 播放线程优化-最优先
     */
    public synchronized ExecutorService executorPlayService() {
        if (executorServicePlay == null) {
            //TODO 线程池的相关概念 需要理解
            //TODO 核心线程 最大线程 非核心线程闲置60秒回收 任务队列
            executorServicePlay = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory("SourceViewModel Dispatcher", false));
        }
        return executorServicePlay;
    }

    public static ThreadFactory threadFactory(final String name, final boolean daemon) {
        return runnable -> {
            Thread result = new Thread(runnable, name);
            result.setDaemon(daemon);
            return result;
        };
    }

    /**
     * 异步获取媒体分类信息。
     * <p>
     * 该方法通过线程池执行一个任务，来获取媒体分类。它避免了在主线程中进行网络请求或耗时操作，
     * 从而提高了应用的响应速度和用户体验。在获取分类信息的过程中，进行了异常处理以避免程序崩溃，
     * 并且在获取结果后，对结果进行了处理和日志记录，最后将结果发布到LiveData中，供观察者使用。
     */
    public void fetchCategory() {
        Future<?> future = execute(() -> {
            try {
                new MediaCategoryTask().call();
            } catch (Exception e) {
                Logger.e(e, "获取异常");
            }
        });
        //
        List<MediaCategory> list = new ArrayList<>();
        MediaCategory category = new MediaCategory();
        category.name = "";
        category.id = "";
        list.add(category);

        MediaResult<List<MediaCategory>> result = MediaResult.success(list);
        categoryResult.postValue(result);
        //
        getAddFutures(future);
    }

    /**
     * 搜索
     *
     * @param siteKey 源
     * @param keyword 搜索关键字
     */
    public void fetchSearch(String siteKey, String keyword) {
        Future<?> future = execute(() -> {
            try {
                new MediaSearchTask(siteKey, keyword).call();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        getAddFutures(future);
    }

    public void fetchRecWords(String keyword) {
        Future<?> future = execute(() -> {
            try {
                Map<String, Object> params = new HashMap<>();
                params.put("url", "https://s.video.qq.com/smartbox");
                params.put("plat", 2);
                params.put("ver", 0);
                params.put("num", 10);
                params.put("otype", "json");
                params.put("query", keyword);
                getKeyword(keyword, params);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        getAddFutures(future);
    }

    /**
     * 获取详情
     *
     * @param siteKey 源
     * @param id      搜索关键字
     */
    public void fetchDetails(String siteKey, String id) {
        Future<?> future = execute(() -> {
            try {
                new MediaDetailsTask(siteKey, id).call();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        getAddFutures(future);
    }

    /**
     * 获取分类列表
     *
     * @param query 分类查询数据
     */
    public void fetchMediaList(MediaQuery query) {
        Future<?> future = execute(() -> {
            try {
                new MediaListTask(query).call();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        getAddFutures(future);
    }

    /***使用线程安全集合*/
    final List<Future<?>> futures = Collections.synchronizedList(new ArrayList<>());
    ;

    /**
     * 获取媒体播放信息(优先
     *
     * @param siteKey     siteKey
     * @param playFlag    playFlag
     * @param progressKey progressKey
     * @param url         url
     */
    public void fetchPlayer(String siteKey, String playFlag, String progressKey, String url) {
        tryClearFutures();
        if (executorService != null) {
            executorService.shutdownNow();
            executorService = null;
        }
        executorPlayService().submit(new MediaPlayTask(siteKey, playFlag, url));
    }

    public static class MediaCategoryTask implements Callable<MediaResult<List<MediaCategory>>> {
        private static final String TAG = "Source.MediaCategoryTask";
        private volatile boolean shouldRun = true;

        public MediaCategoryTask() {

        }

        @Override
        public MediaResult<List<MediaCategory>> call() throws Exception {
            Thread.currentThread().setName(TAG);
            while (!Thread.currentThread().isInterrupted()) {
                Logger.t(TAG).d("MediaCategoryTask call");
                try {
                    MediaResult<List<MediaCategory>> result = AMedia.get().getMediaCategory();
                    // 增加一个默认首页
                    if (result.getData() == null) {
                        result.setData(new ArrayList<>());
                    }
                    MediaCategory home = new MediaCategory();
                    home.id = "my0";
                    home.name = "首页";
                    result.getData().add(0, home);

                    Logger.t(TAG).d("MediaCategory result=\n" + result);
//                    categoryResult.postValue(result);
                    return result;
                } catch (InterruptedException e) {
                    Logger.t(TAG).e(e, "获取MediaCategory时发生线程中断异常");
                    // 捕获中断异常，并记录日志
                    Thread.currentThread().interrupt(); // 重新设置中断标志
                    shouldRun = false; // 设置标志为false，以便尽快退出循环
                } catch (Exception e) {
                    // 优化异常处理，避免泄露敏感信息
                    Logger.t(TAG).e(e, "获取MediaCategory时发生异常");
                } finally {
                    //
                    clear();
                }
            }
            clear();
            return MediaResult.empty();
        }

        // 允许外部调用中断方法
        public void interruptGracefully() {
            shouldRun = false;
            Thread.currentThread().interrupt();
        }

        private void clear() {

        }
    }

    public class MediaListTask implements Callable<MediaResult<List<MediaBean>>> {
        private final String TAG;
        private final MediaQuery query;

        public MediaListTask(MediaQuery query) {
            this.query = query;
            TAG = "Source.MediaListTask-" + query.getSiteKey();
        }

        @Override
        public MediaResult<List<MediaBean>> call() throws Exception {
            // 设置线程名称
            try {
                Thread.currentThread().setName(TAG);
                if (!Thread.currentThread().isInterrupted()) {
                    Logger.t(TAG).d("MediaListTask call");
                    String siteKey = query.getSiteKey();
                    Site site = VodConfig.get().getSite(siteKey);

                    if (siteKey == null || site == null) {//默认
                        site = VodConfig.get().getHome();
                    }
                    if (site == null) {
                        Logger.t(TAG).e("Site configuration is missing.");
                        throw new IllegalArgumentException("Site cannot be null.");

                    }
                    query.setSiteKey(site.getKey());
                    MediaResult<List<MediaBean>> result = null;
                    Logger.t(TAG).d("MediaList query=" + query);
                    result = AMedia.get().getMediaList(query);
                    Logger.t(TAG).d("MediaList result=\n" + result);
                    MediaBeanResult.postValue(result);
                    return result;
                }
            } catch (InterruptedException e) {
                Logger.t(TAG).e(e, "InterruptedException occurred while fetching media list.");
            } catch (Exception e) {
                Logger.t(TAG).e(e, "An error occurred while fetching media list.");
            } finally {
                clear();
            }

            return MediaResult.empty();
        }

        private void clear() {

        }
    }

    //任务==获取详情
    public class MediaDetailsTask implements Callable<MediaResult<List<MediaBean>>> {
        private final String TAG;
        private final String siteKey;
        private final String id;

        public MediaDetailsTask(String siteKey, String id) {
            this.siteKey = siteKey;
            this.id = id;
            this.TAG = "Source.MediaDetailsTask:" + siteKey + "-" + id;
        }

        @Override
        public MediaResult<List<MediaBean>> call() throws Exception {
            try {
                Thread.currentThread().setName(TAG);
                if (!Thread.currentThread().isInterrupted()) {
                    MediaQuery query = MediaQuery.create();
                    query.setType(MediaQuery.TYPE_DETAILS);
                    query.setSiteKey(siteKey);
                    query.setId(id);
                    Logger.t(TAG).d("fetchDetails MediaQuery=\n" + query);
                    MediaResult<List<MediaBean>> result = null;

                    result = AMedia.get().getMediaList(query);
                    Logger.t(TAG).d("fetchDetails result=\n" + result);
                    MediaDetailResult.postValue(result);
                    return result;
                }

            } catch (InterruptedException e) {
                Logger.t(TAG).e(e, "InterruptedException occurred while fetching media details.");
            } finally {
                clear();
            }

            return MediaResult.empty();
        }

        private void clear() {

        }
    }

    public static class MediaSearchTask implements Callable<MediaResult<List<MediaBean>>> {
        private final String TAG;
        private final String siteKey;
        private final String keyword;

        public MediaSearchTask(String siteKey, String keyword) {
            this.siteKey = siteKey;
            this.keyword = keyword;
            this.TAG = "Source.MediaSearchTask:" + siteKey + "-" + keyword;
        }

        @Override
        public MediaResult<List<MediaBean>> call() throws Exception {


            try {
                Thread.currentThread().setName(TAG);
                if (!Thread.currentThread().isInterrupted()) {
                    //分词器，对搜索的关键字进行分词，获取分词结果
                    MediaQuery query = MediaQuery.createBuilder()
                            .setType(MediaQuery.TYPE_SEARCH)
                            .setSiteKey(siteKey)
                            .setKeyword(keyword).build();

                    MediaResult<List<MediaBean>> result = null;
                    result = AMedia.get().getMediaList(query);
//                    MediaSearchResult.postValue(result);
                    return result;
                }

            } catch (InterruptedException e) {
                Logger.t(TAG).e(e, "InterruptedException occurred while searching media.");
            } catch (Exception e) {
                Logger.t(TAG).e(e, "An error occurred while searching media.");
            } finally {
                clear();
            }

            return MediaResult.empty();
        }

        private void clear() {

        }
    }

    /**
     * 获取播放的信息
     */
    public class MediaPlayTask implements Callable<MediaResult<MediaPlay>> {
        private final String TAG;
        private final String siteKey;
        private final String playFlag;
        private final String url;
        private final String progressKey = null;

        MediaPlayTask(String siteKey, String playFlag, String url) {
            this.siteKey = siteKey;
            this.playFlag = playFlag;
            this.url = url;
            this.TAG = "Source.MediaPlayTask:" + siteKey + "-" + playFlag;

        }

        @Override
        public MediaResult<MediaPlay> call() throws Exception {
            try {
                Thread.currentThread().setName(TAG);
                if (!Thread.currentThread().isInterrupted()) {
                    MediaCallback.getDefault().onMessage("#2整理播放请求");
                    MediaQuery query = MediaQuery.createBuilder()
                            .setType(MediaQuery.TYPE_PLAYBACK)
                            .setSiteKey(siteKey)
                            .setPlayFlag(playFlag)
                            .setPlayUrl(url)
                            .build();

                    //停止其他播放请求 TODO
                    Logger.t(TAG).d("getMediaPlayback query=\n" + query);
                    MediaResult<MediaPlay> mediaPlay = AMedia.get().getMediaPlayback(query, null);

                    Logger.t(TAG).d("getMediaPlayback result=\n" + mediaPlay);

                    MediaPlayResult.postValue(mediaPlay);
                    return mediaPlay;
                }

            } catch (InterruptedException e) {
                Logger.t(TAG).e(e, "InterruptedException occurred while fetchPlayer.");
            } catch (Exception e) {
                Logger.t(TAG).e(e, "An error occurred while fetchPlayer.");
            } finally {
                clear();
            }

            return MediaResult.empty();

        }

        private void clear() {

        }
    }


    @Override
    protected void onCleared() {
        super.onCleared();
    }

    public void getKeyword(String key, Map<String, Object> params) {
        RecWordHelper.loadRecWord(key, queryWord);
    }

    private void checkId(String id) {
        //检查ID
        if (id.startsWith("push://")) {
            id.substring(7);
        }
        if (id.isEmpty() || id.startsWith("msearch:")) {
            // 如果没有ID，则会启动搜索
        } else {
            //获取ID
        }
    }

    /**
     * 工具类（委托）
     */
    public static class Media {
        /**
         * 检查是不是播放格式
         */
        public static boolean isVideoFormat(String url) {
            return Sniffer.isVideoFormat(url);
        }

        /**
         * 从中取出播放地址
         *
         * @param text 字串
         * @return 播放地址（如果没有则返回“”）异常则返回null
         */
        public static String getVideoUrl(String text) {
            return Sniffer.getVideoUrl(text);
        }
    }

}